package com.xuecheng.content.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.xuecheng.api.content.model.dto.TeachplanDTO;
import com.xuecheng.api.content.model.dto.TeachplanMediaDTO;
import com.xuecheng.api.media.model.aliyun.dto.MediaDTO;
import com.xuecheng.common.domain.code.CommonErrorCode;
import com.xuecheng.common.domain.response.RestResponse;
import com.xuecheng.common.enums.common.AuditEnum;
import com.xuecheng.common.enums.common.CommonEnum;
import com.xuecheng.common.enums.content.CourseAuditEnum;
import com.xuecheng.common.enums.content.CourseModeEnum;
import com.xuecheng.common.enums.content.TeachPlanEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.content.agent.MediaApiAgent;
import com.xuecheng.content.common.constant.ContentErrorCode;
import com.xuecheng.content.convert.TeachplanConvert;
import com.xuecheng.content.convert.TeachplanMediaConvert;
import com.xuecheng.content.entity.CourseBase;
import com.xuecheng.content.entity.Teachplan;
import com.xuecheng.content.entity.TeachplanMedia;
import com.xuecheng.content.entity.ext.TeachplanNode;
import com.xuecheng.content.mapper.TeachplanMapper;
import com.xuecheng.content.service.CourseBaseService;
import com.xuecheng.content.service.TeachplanMediaService;
import com.xuecheng.content.service.TeachplanService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

import static com.xuecheng.common.enums.content.TeachPlanEnum.FIRST_PARENTID_FLAG;

/**
 * 课程计划 服务实现类
 *
 * @author dongliang
 */
@Slf4j
@Service
public class TeachplanServiceImpl extends ServiceImpl<TeachplanMapper, Teachplan> implements TeachplanService {


    @Autowired
    private MediaApiAgent mediaApiAgent;
    @Autowired
    private CourseBaseService courseBaseService;
    @Autowired
    private TeachplanMediaService teachplanMediaService;

    /**
     * 根据课程Id查询课程计划（树形结构）
     *
     * @param courseId  课程id
     * @param companyId 公司id
     * @return 带树形的课程计划结果
     */
    public TeachplanDTO queryTreeNodesByCourseId(Long courseId, Long companyId) {
        //业务分析：
        //1.判断关键数据 courseId companyId
        if (ObjectUtils.isEmpty(companyId) || ObjectUtils.isEmpty(courseId)) {
            ExceptionCast.cast(ContentErrorCode.E_120018);
        }
        //2.判断业务数据
        //课程基础信息
        //判断是否存在
        //判断是否是同一家教学机构
        LambdaQueryWrapper<CourseBase> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CourseBase::getId, courseId);
        wrapper.eq(CourseBase::getCompanyId, companyId);
        //查询课程基础表来判断
        CourseBase courseBase = courseBaseService.getOne(wrapper);

        if (ObjectUtils.isEmpty(courseBase)) {
            throw new RuntimeException("查询课程基础信息为空");
        }

        Integer status = courseBase.getStatus();
        if (CommonEnum.DELETE_FLAG.getCode().equals(status)) {
            throw new RuntimeException("该课程已被删除");
        }

        //3.根据Courseid查询课程计划数据
        List<TeachplanNode> nodes = this.baseMapper.selectByCourseId(courseId);

        if (!ObjectUtils.isEmpty(nodes)) {
            //4.通过java的递归生成课程计划树形结构
            //第一节点
            TeachplanNode parentNode = nodes.remove(0);

            //递归方法
            recursiveMode(parentNode, nodes);

            //5.将数据转为dto并返回
            TeachplanDTO teachplanDTO = TeachplanConvert.INSTANCE.node2dto(parentNode);
            return teachplanDTO;
        }

        return null;
    }

    /**
     * 创建或修改课程计划信息
     *
     * @param dto 课程计划信息
     * @return TeachplanDTO 课程计划信息
     */
    @Transactional
    public TeachplanDTO createOrModifyTeachPlan(TeachplanDTO dto, Long companyId) {
    /*
     * 业务分析：
            //1.判断业务操作 判断是创建还是修改：teachplanId 如果有:修改 如果没有:创建     */
        TeachplanDTO resutlDTO = null;
        if (ObjectUtils.isEmpty(dto.getTeachPlanId())) {
            //创建
            resutlDTO = createTeachplan(dto, companyId);
        } else {
            //修改
            resutlDTO = modifyTeachplan(dto, companyId);
        }

        return resutlDTO;
    }


    /**
     * 创建课程计划信息
     *
     * @param dto 课程计划信息
     * @return TeachplanDTO 课程计划信息
     */
    private TeachplanDTO createTeachplan(TeachplanDTO dto, Long companyId) {
        /*
         * 主体方法业务分析：
         *   1.判断关键数据
         *   2.判断业务数据
         */
        CourseBase courseBase = verifyTeachplanMsg(dto, companyId);


        Teachplan teachplan = TeachplanConvert.INSTANCE.dto2entity(dto);

        //3.判断新增数据是二级还是三级课程计划保存数据库并返回父级数据
        Teachplan parentNodeTeachplan = generateParentNode(dto, courseBase);

        /*   获得父级数据并进行关联
         *       parentid
         *       grade：子级grade是父级grade+1
         *       orderby：获得父级课程计划的子级数据个数+1（bug）*/
        teachplan.setParentid(parentNodeTeachplan.getId());
        teachplan.setGrade(parentNodeTeachplan.getGrade() + 1);
        // select count(1) from teachplan where parentId = parentNode.getId();
        LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Teachplan::getParentid, parentNodeTeachplan.getId());

        int count = this.count(queryWrapper);
        teachplan.setOrderby(count + 1);

        // 4.将dto转为po并保存
        boolean result = this.save(teachplan);

        // 5.判断保存后的结果
        if (!result) {
            ExceptionCast.cast(ContentErrorCode.E_120407);
        }

        // 6.将数据库最新数据转为dto并返回
        Teachplan po = this.getById(teachplan.getId());

        TeachplanDTO resultDTO = TeachplanConvert.INSTANCE.entity2dto(po);

        return resultDTO;


    }

    /**
     * 修改课程计划信息
     *
     * @param dto 课程计划信息
     * @return TeachplanDTO 课程计划信息
     */
    private TeachplanDTO modifyTeachplan(TeachplanDTO dto, Long companyId) {
        /*
         * 主体方法业务分析：
         *   1.判断关键数据
         *   2.判断业务数据
         */
        CourseBase courseBase = verifyTeachplanMsg(dto, companyId);
        /*课程计划
                判断是否存在*/
        LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Teachplan::getId, dto.getTeachPlanId());
        int count = this.count(queryWrapper);
        if (count < 1) {
            ExceptionCast.cast(ContentErrorCode.E_120402);
        }


        //3.修改课程计划数据
        //              pname starttime endtime is_preview
        LambdaUpdateWrapper<Teachplan> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(Teachplan::getPname, dto.getPname());
        updateWrapper.set(Teachplan::getStartTime, dto.getStartTime());
        updateWrapper.set(Teachplan::getEndTime, dto.getEndTime());
        updateWrapper.set(Teachplan::getIsPreview, dto.getIsPreview());
        updateWrapper.eq(Teachplan::getId, dto.getTeachPlanId());
        boolean updateResult = this.update(updateWrapper);
        //4.判断修改后的结果
        if (!updateResult) {
            ExceptionCast.cast(ContentErrorCode.E_120407);
        }
        // 5.将数据库最新的数据转为dto并返回
        Teachplan teachplan = this.getById(dto.getTeachPlanId());
        TeachplanDTO teachplanDTO = TeachplanConvert.INSTANCE.entity2dto(teachplan);

        return teachplanDTO;
    }

    /**
     * 根据课程计划Id删除课程计划信息
     *
     * @param teachPlanId {@link Long} 课程计划Id
     * @param companyId   {@link Long} 公司Id
     */
    public void removeTeachPlan(Long teachPlanId, Long companyId) {
        //判断课程计划是否存在
        Teachplan teachplan = this.getById(teachPlanId);
        if (ObjectUtils.isEmpty(teachplan)) {
            ExceptionCast.cast(ContentErrorCode.E_120401);
        }
        //判断基本课程是否为空
        CourseBase courseBase = courseBaseService.getById(teachplan.getCourseId());
        if (ObjectUtils.isEmpty(courseBase)) {
            ExceptionCast.cast(ContentErrorCode.E_120013);
        }

        //1.3 判断公司 Id是否和课程中的公司Id一致
        if (!(ObjectUtils.nullSafeEquals(courseBase.getCompanyId(), companyId))) {
            ExceptionCast.cast(CommonErrorCode.E_100108);
        }

        //删除
        // 2. 根据课程计划等级进行业务判断
        if (teachplan.getGrade() == TeachPlanEnum.SECEND_LEVEL) {
            //判断1 大章节 有小章节
            //判断大章节是否存在
            LambdaQueryWrapper<Teachplan> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(Teachplan::getId, teachPlanId);

            int minCount = this.count(wrapper);

            if (minCount > 0) {
                ExceptionCast.cast(ContentErrorCode.E_120409);
            }
        } else {
            //判断小章节下是否关联视频资源

            LambdaQueryWrapper<TeachplanMedia> mediaWrapper = new LambdaQueryWrapper<>();
            mediaWrapper.eq(TeachplanMedia::getTeachplanId, teachPlanId);

            int mediaCount = teachplanMediaService.count(mediaWrapper);

            if (mediaCount > 0) {
                ExceptionCast.cast(ContentErrorCode.E_120414);
            }
            removeById(teachPlanId);
        }
    }

    /**
     * 递归生成树形结构数据
     *
     * @param parentNode 当前的父级
     * @param nodes      集合数据
     */
    private void recursiveMode(TeachplanNode parentNode, List<TeachplanNode> nodes) {
        //判断父节点的子节点是否为空
        if (ObjectUtils.isEmpty(parentNode.getTeachPlanTreeNodes())) {
            parentNode.setTeachPlanTreeNodes(new ArrayList<TeachplanNode>(8));
        }
        //遍历所有的list数据
        for (TeachplanNode node : nodes) {

            //根据子节点的parentid和上一节的id绑定赋值
            if (ObjectUtils.nullSafeEquals(node.getParentid(), parentNode.getId())) {
                parentNode.getTeachPlanTreeNodes().add(node);
                //递归把所有的子节点赋值

                //到第三层就停止递归
                if (!(node.getGrade().equals(TeachPlanEnum.THIRD_LEVEL))) {
                    recursiveMode(node, nodes);
                }else {
                    LambdaQueryWrapper<TeachplanMedia> queryWrapper = new LambdaQueryWrapper<>();
                    queryWrapper.eq(TeachplanMedia::getTeachplanId,node.getId());
                    queryWrapper.eq(TeachplanMedia::getCourseId,node.getCourseId());

                    TeachplanMedia teachplanMedia = teachplanMediaService.getOne(queryWrapper);

                    if (!(ObjectUtils.isEmpty(teachplanMedia))){
                        node.setTeachplanMedia(teachplanMedia);
                    }
                }
            }
        }
    }

    /*   1.判断关键数据
     *   2.判断业务数据
     *       课程基础信息
     *           判断是否存在
     *           判断是否同一家机构
     *           判断是否删除
     *           判断课程基础信息审核状态：未提交和审核未通过
     */
    private CourseBase verifyTeachplanMsg(TeachplanDTO dto, Long companyId) {
        if (ObjectUtils.isEmpty(dto.getPname()) ||
                ObjectUtils.isEmpty(dto.getCourseId()) ||
                ObjectUtils.isEmpty(companyId)
        ) {
            ExceptionCast.cast(ContentErrorCode.E_120401);
        }

        LambdaQueryWrapper<CourseBase> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CourseBase::getId, dto.getCourseId());
        wrapper.eq(CourseBase::getCompanyId, companyId);
        wrapper.eq(CourseBase::getStatus, CommonEnum.USING_FLAG.getCode());
        //查询课程基础表来判断
        CourseBase courseBase = courseBaseService.getOne(wrapper);

        if (ObjectUtils.isEmpty(courseBase)) {
            throw new RuntimeException("查询课程基础信息为空");
        }

        Integer status = courseBase.getStatus();
        if (CommonEnum.DELETE_FLAG.getCode().equals(status)) {
            throw new RuntimeException("该课程已被删除");
        }
        //判断审批状态未提交.审批未通过才可修改
        String auditStatus = courseBase.getAuditStatus();
        if (CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(auditStatus) ||
                CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus) ||
                CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(auditStatus)
        ) {
            ExceptionCast.cast(ContentErrorCode.E_120011);
        }
        return courseBase;
    }

    /*
     * 业务分析：
     *   1.判断新增数据是二级还是三级课程计划
     *       判断新增数据是否有parentId值
     *           如果有：三级课程计划
     *           如果没有：二级课程计划
     *   2.获得二级课程计划的父级数据
     *       查询数据库获得一级课程计划：courseId、parentid=0
     *           如果没有：自动创建并保存数据--数据来源 coursebase
     *           如果有：直接返回
     *   3.获得三级课程计划的父级数据
     *       查询数据库获得二级课程计划：parentId
     *           如果没有：直接抛出异常
     *           如果有：直接返回
     *
     *
     * */
    private Teachplan generateParentNode(TeachplanDTO dto, CourseBase courseBase) {
        if (ObjectUtils.isEmpty(dto.getParentid())) {
            //没有：二级课程计划
            LambdaQueryWrapper<Teachplan> baseWrapper = new LambdaQueryWrapper<>();
            baseWrapper.eq(Teachplan::getCourseId, courseBase.getId());
            baseWrapper.eq(Teachplan::getParentid, TeachPlanEnum.FIRST_PARENTID_FLAG);
            Teachplan parentTeachplan = this.getOne(baseWrapper);

            //判断1级数据是否存在
            if (ObjectUtils.isEmpty(parentTeachplan)) {
                //不存在
                parentTeachplan = new Teachplan();
                parentTeachplan.setPname(courseBase.getName());
                parentTeachplan.setParentid(new Long(TeachPlanEnum.FIRST_PARENTID_FLAG));
                parentTeachplan.setGrade(TeachPlanEnum.FIRST_LEVEL);
                parentTeachplan.setDescription(courseBase.getDescription());
                parentTeachplan.setOrderby(TeachPlanEnum.FIRST_LEVEL);
                parentTeachplan.setCourseId(courseBase.getId());
                boolean save = this.save(parentTeachplan);

                if (!save) {
                    ExceptionCast.cast(ContentErrorCode.E_120415);
                }
            }
            return parentTeachplan;
        } else {
            //有：三级课程计划 根据3级课程计划的parentid查询2级课程计划数据

            //判断2级课程计划是否存在
            LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Teachplan::getId, dto.getParentid());

            Teachplan teachplan = this.getOne(queryWrapper);

            if (ObjectUtils.isEmpty(teachplan)) {
                ExceptionCast.cast(ContentErrorCode.E_120408);
            }

            return teachplan;
        }
    }


    /*
     * 业务分析：
     *   1.判断关键数据
     *       teachplanid mediaid
     *
     *   2.判断业务数据
     *       课程计划数据
     *           判断是否存在
     *           判断课程计划是否是第三级
     *           判断是否是同一家机构的操作
     *               根据课程基础信息来查询
     *               判断课程类型是否是点播
     *       媒资数据
     *           判断是否存在
     *           判断是否是同一家机构
     *           判断媒资信息是否审核通过
     *
     *   3.保存课程计划和媒资信息关联关系
     *       判断关联数据是否存在
     *           如果不存在
     *               创建数据
     *           如果存在
     *               修改数据
     *                   mediaid 和 medianame
     *
     *
     *   4.将数据封装成dto并返回
     * */
    @Transactional
    public TeachplanMediaDTO associateMedia(TeachplanMediaDTO dto, Long companyId) {

        // 1.判断关键数据
        //      teachplanid mediaid companyId
        if (ObjectUtils.isEmpty(dto.getTeachplanId()) ||
                ObjectUtils.isEmpty(dto.getMediaId()) ||
                ObjectUtils.isEmpty(companyId)
        ) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }


        //  2.判断业务数据
        //      课程计划数据
        //          判断是否存在
        //          判断课程计划是否是第三级
        LambdaQueryWrapper<Teachplan> teachplanQueryWrapper = new LambdaQueryWrapper<>();

        teachplanQueryWrapper.eq(Teachplan::getId, dto.getTeachplanId());
        teachplanQueryWrapper.eq(Teachplan::getGrade, TeachPlanEnum.THIRD_LEVEL);

        Teachplan teachplan = this.getOne(teachplanQueryWrapper);

        if (ObjectUtils.isEmpty(teachplan)) {
            ExceptionCast.cast(ContentErrorCode.E_120402);
        }


        //       课程基础信息
        //          判断是否是同一家机构的操作
        //              根据课程基础信息来查询
        //              判断课程类型是否是点播
        LambdaQueryWrapper<CourseBase> baseQueryWrapper = new LambdaQueryWrapper<>();

        baseQueryWrapper.eq(CourseBase::getId, teachplan.getCourseId());
        baseQueryWrapper.eq(CourseBase::getCompanyId, companyId);
        baseQueryWrapper.eq(CourseBase::getTeachmode, CourseModeEnum.COURSE_MODE_RECORD_STATUS.getCode());
        baseQueryWrapper.eq(CourseBase::getStatus, CommonEnum.USING_FLAG.getCodeInt());

        CourseBase courseBase = courseBaseService.getOne(baseQueryWrapper);

        // 课程计划和媒资信息绑定要求课程审核状态为：未提交和审核未通过
        String auditStatus = courseBase.getAuditStatus();
        if (CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus) ||
                CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(auditStatus) ||
                CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(auditStatus)
        ) {
            ExceptionCast.cast(ContentErrorCode.E_120015);
        }


        //      媒资数据
        //          判断是否存在
        //          判断是否是同一家机构
        //          判断媒资信息是否审核通过
        RestResponse<MediaDTO> restResponse = mediaApiAgent.getMediaById(dto.getMediaId());

        if (!(restResponse.isSuccessful())) {
            ExceptionCast.castWithCodeAndDesc(restResponse.getCode(), restResponse.getMsg());
        }

        MediaDTO mediaDTO = restResponse.getResult();

        if (!(ObjectUtils.nullSafeEquals(mediaDTO.getCompanyId(), companyId))) {
            ExceptionCast.cast(CommonErrorCode.E_100108);
        }

        String mediaAuditStatus = mediaDTO.getAuditStatus();

        //审核通过
        if (!(AuditEnum.AUDIT_PASTED_STATUS.getCode().equals(mediaAuditStatus))) {
            ExceptionCast.cast(ContentErrorCode.E_120022);
        }

        //  3.保存课程计划和媒资信息关联关系
        //      判断关联数据是否存在
        LambdaQueryWrapper<TeachplanMedia> teachplanMediaQueryWrapper = new LambdaQueryWrapper<>();
        teachplanMediaQueryWrapper.eq(TeachplanMedia::getTeachplanId, teachplan.getId());
        teachplanMediaQueryWrapper.eq(TeachplanMedia::getCourseId, teachplan.getCourseId());

        int count = teachplanMediaService.count(teachplanMediaQueryWrapper);

        boolean result = false;
        if (count < 1) {
            //          如果不存在
            //              创建数据
            TeachplanMedia teachplanMedia = new TeachplanMedia();
            teachplanMedia.setMediaId(mediaDTO.getId());
            teachplanMedia.setTeachplanId(teachplan.getId());
            teachplanMedia.setCourseId(teachplan.getCourseId());
            teachplanMedia.setMediaFilename(mediaDTO.getFilename());

            result = teachplanMediaService.save(teachplanMedia);


        } else {
            //          如果存在
            //              修改数据
            //                  mediaid 和 medianame
            LambdaUpdateWrapper<TeachplanMedia> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.set(TeachplanMedia::getMediaId, mediaDTO.getId());
            updateWrapper.set(TeachplanMedia::getMediaFilename, mediaDTO.getFilename());
            updateWrapper.eq(TeachplanMedia::getTeachplanId, teachplan.getId());

            result = teachplanMediaService.update(updateWrapper);
        }

        if (!result) {
            ExceptionCast.cast(ContentErrorCode.E_120411);
        }


        //  4.将数据封装成dto并返回
        LambdaQueryWrapper<TeachplanMedia> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TeachplanMedia::getTeachplanId, teachplan.getId());

        TeachplanMedia teachplanMedia = teachplanMediaService.getOne(queryWrapper);

        TeachplanMediaDTO resultDTO = TeachplanMediaConvert.INSTANCE.entity2dto(teachplanMedia);

        return resultDTO;
    }
}
